package utils

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"cashway.com/cis/assistant/model"
)

// 打包成zip文件
func Zip(params *model.CompressParams) error {

	// 使用绝对路径操作
	absSourcePath, err := filepath.Abs(params.SourcePath)
	if err != nil {
		return err
	}

	var outputPath string

	// 如果文件名不存在，则使用文件夹名称作为输出的压缩文件名
	if params.OutputFilePath == "" {
		filenameAll := filepath.Base(absSourcePath)
		fileSuffix := filepath.Ext(filenameAll)
		filename := strings.TrimSuffix(filenameAll, fileSuffix)
		outputPath = filepath.Dir(absSourcePath)
		params.OutputFilePath = filepath.Join(outputPath, filename+".zip")
	} else {
		outputPath = filepath.Dir(params.OutputFilePath)
	}

	// 使用绝对路径操作
	absOutputFilePath, err := filepath.Abs(params.OutputFilePath)
	if err != nil {
		return err
	}

	// 文件已存在
	if IsFile(absOutputFilePath) {
		return fmt.Errorf("%s 文件已经存在", absOutputFilePath)
	}

	MkdirAllIfNotExists(outputPath, os.ModePerm)

	// 创建：zip文件
	zipfile, err := os.Create(absOutputFilePath)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	// 打开：zip文件
	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	if IsDir(absSourcePath) {
		// 遍历路径信息
		filepath.Walk(absSourcePath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			// 如果是源路径，提前进行下一个遍历
			if path == absSourcePath {
				return nil
			}

			// 获取：文件头信息
			header, err := zip.FileInfoHeader(info)
			if err != nil {
				return err
			}

			relPath, err := filepath.Rel(absSourcePath, path)
			if err != nil {
				return err
			}
			header.Name = relPath

			// 判断：文件是不是文件夹
			if info.IsDir() {
				header.Name += "/"
			} else {
				// 设置：zip的文件压缩算法
				header.Method = params.Grade
			}

			// 创建：压缩包头部信息
			writer, err := archive.CreateHeader(header)
			if err != nil {
				return err
			}

			if info.IsDir() {
				return nil
			}

			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()

			_, copyErr := io.Copy(writer, file)
			if copyErr != nil {
				return copyErr
			}

			return nil
		})
	} else {
		info, err := os.Stat(absSourcePath)
		if err != nil {
			return err
		}

		// 获取：文件头信息
		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		header.Name = info.Name()
		header.Method = params.Grade

		// 创建：压缩包头部信息
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		file, err := os.Open(absSourcePath)
		if err != nil {
			return err
		}
		defer file.Close()

		_, copyErr := io.Copy(writer, file)
		if copyErr != nil {
			return copyErr
		}
	}

	return nil
}

// 解压zip文件
func UnZip(params *model.DecompressParams) error {

	// 使用绝对路径操作
	absSourceFilePath, err := filepath.Abs(params.SourceFilePath)
	if err != nil {
		return err
	}

	zipReader, err := zip.OpenReader(absSourceFilePath)
	if err != nil {
		return err
	}
	defer zipReader.Close()

	// 如果文件名不存在，则使用文件夹名称作为输出的压缩文件名
	if params.TargetPath == "" {
		filenameAll := filepath.Base(absSourceFilePath)
		fileSuffix := filepath.Ext(filenameAll)
		filename := strings.TrimSuffix(filenameAll, fileSuffix)
		outputPath := filepath.Dir(absSourceFilePath)
		params.TargetPath = filepath.Join(outputPath, filename)
	}

	MkdirAllIfNotExists(params.TargetPath, os.ModePerm)

	for _, file := range zipReader.File {

		path := filepath.Join(params.TargetPath, file.Name)

		// 如果是目录，就创建目录
		if file.FileInfo().IsDir() {
			MkdirAllIfNotExists(path, file.Mode())
			// 因为是目录，跳过当前循环，因为后面都是文件的处理
			continue
		}

		// 文件名冲突，且不支持覆写
		if !params.IsOverwrite && IsFile(path) {
			return fmt.Errorf("存在同名的文件，且不支持覆写: %s", path)
		}

		// 获取到 Reader
		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		fileWriter, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer fileWriter.Close()

		_, fileCopyErr := io.Copy(fileWriter, fileReader)
		if fileCopyErr != nil {
			return fileCopyErr
		}
	}

	return nil
}
